﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EnvDTE;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell.Design;
using System.ComponentModel.Design;
using Microsoft.Samples.LinqToCodeModel.Extensions;
using AutomationFoundation.CodeToolKit.VisualStudio.Linq;
namespace MVC4WPF.Automation.Data
{
    /// <summary>
    /// Helper class that will generate information about contracts related to a MVC4WPF project.
    /// </summary>
    public static class ContractHelper
    {
        public static List<ContractType> GetContractsBasedOnContract(IVsSolution solution, DynamicTypeService loadedTypes, Project sourceProject, Type contractBasedOn)
        {
            List<ContractType> compiledContracts = GetCompiledContracts(solution, loadedTypes, sourceProject, contractBasedOn);
            List<ContractType> codeContracts = GetProjectContracts(sourceProject, contractBasedOn);
            List<ContractType> uniqueContracts = compiledContracts.Union(from code in codeContracts
                            where !(from compiled in compiledContracts select compiled.Name).Contains(code.Name)
                            select code).ToList();

            return uniqueContracts;           
        }

        /// <summary>
        /// Gets the list of intraface contracts that have been compiled and available either in the project or from external referenced dll's.
        /// </summary>
        /// <param name="solution">Interface to the solution struction. Need to use GetService to get the IVsSolution.</param>
        /// <param name="loadedTypes">DynamicTypeService tracks all types that are loaded for the solution. Have to use GetService to get the DynamicType Service.</param>
        /// <param name="sourceProject">The project this dialog is supporting.</param>
        /// <param name="contractBasedOn">The type of the base contract interface that should be collected.</param>
        /// <returns>List of ContractType</returns>
        private static List<ContractType> GetCompiledContracts(IVsSolution solution, DynamicTypeService loadedTypes, Project sourceProject, Type contractBasedOn)
        {
            IVsHierarchy ivh = null;

            try
            {
                solution.GetProjectOfUniqueName(sourceProject.UniqueName, out ivh);
            }
            catch (System.Exception)
            {
                return new List<ContractType>();
            }

            if (ivh != null)
            {

                ITypeDiscoveryService typeTracker = loadedTypes.GetTypeDiscoveryService(ivh);

                IList<System.Type> data = typeTracker.GetTypes(typeof(object), true) as List<System.Type>;

                var testResults = from d in data
                                  where d.IsInterface && d.FindInterfaces(InterfaceTypeFilter, contractBasedOn).Count() != 0
                                  select new ContractType { IsType = true, Name = d.Name, Namespace = d.Namespace, TypeForContract = d, FullName = d.FullName };

                return testResults.ToList<ContractType>();

            }

            return new List<ContractType>();
        }


        /// <summary>
        /// Gets all interfaces from the project that inherit from the supplied contractBasedOn
        /// </summary>
        /// <param name="sourceProject">The project that is being searched</param>
        /// <param name="contractBasedOn">The contracts that are based on the supplied interface contract type.</param>
        /// <returns>List of contract types.</returns>
        private static List<ContractType> GetProjectContracts(Project sourceProject, Type contractBasedOn)
        {
            if (sourceProject == null)
            {
                return new List<ContractType>();
            }

            ProjectItems data = sourceProject.ProjectItems;

            var codeClasses = QueryableProjectItems.GetElementsWhere<ProjectItem>(data, P => P.Kind == ProjectItemTypes.PhysicalFile);

            List<ContractType> interfaces = new List<ContractType>();

            foreach (var item in codeClasses)
            {
                if (item.FileCodeModel != null)
                {
                    var supportedInterfaces = from I in item.FileCodeModel.GetIEnumerable<CodeInterface>()
                                              where I.GetImplementedInterfaces().Count(C => C.FullName == contractBasedOn.FullName) == 1
                                              select new ContractType { Name = I.Name, Namespace = I.Namespace.Name, IsType = false, RawInterface = I  };
                    interfaces.AddRange(supportedInterfaces);
                }
            }
             

            return interfaces;
        }

        /// <summary>
        /// Filter delegate used to determine if an interface is of the correct type.
        /// </summary>
        /// <param name="t">The type being checked</param>
        /// <param name="filter">The type to be used as the filter</param>
        /// <returns>Boolean if the interface is the correct type.</returns>
        public static bool InterfaceTypeFilter(Type t, object filter)
        {
            if (t.FullName == ((Type)filter).FullName)
            {
                return true;
            }

            return false;
        }
    }

}
